#ifndef _MSGQUEUE_H_
#define _MSGQUEUE_H_

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>

#include <stdio.h>

class MessageQueue {

	bool created;
	bool send_ok;
	bool receive_ok;

	int msgqid;

public:

	/**
	 * Crea un objeto MessageQueue sin una cola del sistema asociada.
	 */
	MessageQueue():
		created(false),
		send_ok(false),
		receive_ok(false) {
	}

	/**
	 * Crea un objeto MessageQueue y le asocia la cola del sistema
	 * identificada por el valor de key. Si create==true intenta crear
	 * la cola, en otro caso intenta obtener una cola ya existente.
	 */
	MessageQueue(key_t key, bool create=false):
		created(false),
		send_ok(true),
		receive_ok(true) {
		int flags = (create ? (IPC_CREAT | IPC_EXCL) : 0) | 0600;
		msgqid = msgget(key, flags);
		created = (msgqid != -1);
	}

	/**
	 * Envía un mensaje a través de la cola. El valor del campo mtype
	 * del mensaje pasado será type.
	 */
	template <typename T>
	void send(T* obj, long type) {
		struct {
			long mtype;
			T mtext;
		} msg;
		msg.mtype = type;
		msg.mtext = *obj;

		send_ok = (msgsnd(msgqid, &msg, sizeof(T), 0) != -1);

		if(!send_ok && errno==EIDRM) {
			msgqid = -1;
			created = false;
		}
	}

	/**
	 * Recibe un mensaje a través de la cola. El parámetro type tiene
	 * el mismo significado que el parámetro msgtyp de la llamada
	 * al sistema msgrcv.
	 */
	template <typename T>
	long recv(T* obj, long type=0, bool except=false, bool nowait=false) {
		struct {
			long mtype;
			T mtext;
		} msg;
		
		if( msgrcv(msgqid, &msg, sizeof(T), type,
				(nowait ? IPC_NOWAIT : 0) | (except ? MSG_EXCEPT : 0)) != -1) {
			*obj = msg.mtext;
			receive_ok = true;
			return msg.mtype;
		} else {
			receive_ok = false;
			if(errno==EIDRM) {
				msgqid = -1;
				created = false;
			}
			return -1;
		}
	}

	/**
	 * Libera los recursos ocupados por la cola del sistema.
	 */
	void remove() {
		msgctl(msgqid, IPC_RMID, (struct msqid_ds*) 0);
		msgqid = -1;
		created = false;
	}

	/**
	 * Devuelve el estado de la cola. Devuelve true si y solo si
	 * el objeto MessageQueue está asociado a una cola del sistema, y
	 * la última invocación a send fue satisfactoria, y la última
	 * invocación a recv fue satisfactoria.
	 */
	operator bool() {
		return created && send_ok && receive_ok;
	}

};

#endif
